﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using IMLibrary.WS;
using IMLibrary.WS.Net;
using IMLibrary.WS.Server;
using IMLibrary.Net.Enum; 
using IMLibrary.BinaryPacket;
using IMLibrary.Server;
using Ourmsg.FileTransmit;
 

namespace Ourmsg.Server
{
    /// <summary>
    /// 离线文件服务器
    /// </summary>
    public class OfflineFileServer :WebSocketService
    {
        /// <summary>
        /// 离线文件服务器
        /// </summary>
        /// <param name="Port">服务端口</param>
        public OfflineFileServer(int port)
        {
            #region websocket
            Path = "/OfflineFiles";
            WSServer = new WebSocketServer(port);
            WSServer.KeepClean = true;
            WSServer.AddWebSocketService<WebSocketSession>(Path, () => new WebSocketSession(this));
            WSServer.AuthenticationSchemes = IMLibrary.WS.Net.AuthenticationSchemes.Basic;
            WSServer.Realm = "OurMsg Server";
            WSServer.UserCredentialsFinder = ID =>
            {
                var name = ID.Name;
                // Return user name, password, and roles.If the user credentials aren't found,null.
                return name == "Ourmsg" ? new IMLibrary.WS.Net.NetworkCredential(name, "123456@Ourmsg.net") : null;
            };
            #endregion
        }

        #region 属性  
        /// <summary>
        /// 文件中转服务时钟
        /// </summary>
        private System.Timers.Timer TimerFileServer = null; 
        /// <summary>
        /// 保存离线文件夹的名称
        /// </summary>
        const  string OfflineFolder = "OfflineFiles";
        /// <summary>
        /// 缓存文件夹
        /// </summary>
        const string CacheFolder = OfflineFolder + @"\Cache";
        /// <summary>
        /// 
        /// </summary>
        bool IsCheck = false; 
        #endregion

        #region 事件
        ///// <summary>
        ///// 客户端离线文件上传完成事件
        ///// </summary>
        ///// <param name="offlineFileMsg"></param>
        //public delegate void offlineFileEventHandler(OfflineFileMsg offlineFileMsg);
        ///// <summary>
        ///// 客户端离线文件上传完成事件
        ///// </summary>
        //public event offlineFileEventHandler FileUploaded;
       
        /// <summary>
        /// 触发客户端离线文件上传完成事件
        /// </summary>
        /// <param name="offlineFileMsg"></param>
       // private void onFileUploaded(OfflineFileMsg offlineFileMsg)
       // {
       //     if (FileUploaded != null)
       //         FileUploaded(offlineFileMsg);
       // }


       // /// <summary>
       // /// 
       // /// </summary>
       // public event offlineFileEventHandler FileDownloaded;
       ///// <summary>
       ///// 触发客户端离线文件上传完成事件
       ///// </summary>
       ///// <param name="offlineFileMsg"></param>
       // private void onFileDownloaded(OfflineFileMsg offlineFileMsg)
       // {
       //     if (FileDownloaded != null)
       //         FileDownloaded(offlineFileMsg);
       // }
        #endregion

        #region 公共方法

        #region 开始服务
        /// <summary>
        /// 开始服务
        /// </summary>
        public void Start()
        {
            #region 创建文件夹
            {
                System.IO.DirectoryInfo dInfo = new System.IO.DirectoryInfo(OfflineFolder);
                if (!dInfo.Exists)
                    dInfo.Create();
                System.IO.DirectoryInfo cInfo = new System.IO.DirectoryInfo(OfflineFolder + @"\Cache");
                if (!cInfo.Exists)
                    cInfo.Create();
            }
            #endregion

            #region 时钟服务
            if (TimerFileServer == null)
            {
                TimerFileServer = new System.Timers.Timer();
                TimerFileServer.Elapsed += new System.Timers.ElapsedEventHandler(TimerFileServer_Elapsed);
            }

            TimerFileServer.Interval = 2000;//设置文件缓存时间 （默认1分钟）
            TimerFileServer.Enabled = true;//开始服务
            #endregion

            WSServer.Start();
        }

        public override void OnClose(WebSocketSession session, CloseEventArgs e)
        { 
            var tf = session.Tag as OfflineFileInfo;
            if (tf != null)
            {
                bool t = false;
                if (tf.CurrLength < tf.FS.Length)
                    t = true;
                CloseFileStream(tf.FS);
                if (t)
                {
                    var CacheFullFileName = CacheFolder + @"\" + tf.MD5 + tf.Extension;//标记文件路径
                    if (File.Exists(CacheFullFileName))
                        File.Delete(CacheFullFileName);
                }
            }
        }

        public override void OnMessage(WebSocketSession session, MessageEventArgs e)
        { 
            if (e.RawData.Length < FilePacket.HeaderLength)  //收到非法消息
            {
                Sessions.CloseSession(session.ID);
                return;
            }

            FilePacket fp = new FilePacket(e.RawData);
            switch (fp.CommandType)
            {
                case (byte)FileServerCommandType.New://创建文件请求
                    {
                        if (fp.PayloadLength < BinaryFileMsg.HeaderLength)
                        {
                            Sessions.CloseSession(session.ID);
                            return;
                        }

                        BinaryFileMsg FileMsg = new BinaryFileMsg(fp.Payload);
                        string fullFileName = OfflineFolder + @"\" + FileMsg.MD5 + FileMsg.Extension;//标记文件路径
                        string CacheFullFileName = CacheFolder + @"\" + FileMsg.MD5 + FileMsg.Extension;//标记文件路径
                        if (File.Exists(fullFileName))//如果文件已在服务器硬盘中
                        {
                            fp.CommandType = (byte)FileServerCommandType.Over;//标识文件已经传输完成
                            Sessions.SendTo(fp.Header, session.ID);//通知客户端停止上传
                            Sessions.CloseSession(session.ID);
                        }
                        else if (File.Exists(CacheFullFileName))//如果缓存中已有文件，说明他人正在上传同一文件
                        {
                            fp.CommandType = (byte)FileServerCommandType.Else;//标识文件已经传输完成
                            Sessions.SendTo(fp.Header, session.ID);//通知客户端停止上传
                            Sessions.CloseSession(session.ID);
                        }
                        else
                        {
                            #region 创建缓存文件
                            OfflineFileInfo tf = new OfflineFileInfo();
                            tf.MD5 = FileMsg.MD5;
                            tf.Extension = FileMsg.Extension;
                            tf.Length = FileMsg.Length;
                            tf.FS = new FileStream(CacheFullFileName, FileMode.Append, FileAccess.Write, FileShare.Read);
                            session.Tag = tf;
                            #endregion

                            ///////////////////////
                            //通知用户上传文件
                            fp.CommandType = (byte)FileServerCommandType.Set;
                            Sessions.SendTo(fp.Header, session.ID);//通知客户端上传
                            ///////////////////////
                        }
                    }
                    break;
                case (byte)FileServerCommandType.Set://上传文件请求
                    {
                        var tf = session.Tag as OfflineFileInfo;
                        if (tf == null)
                        {
                            Sessions.CloseSession(session.ID);
                            return;
                        }
                        else
                        {
                            //fp.Payload = Convert.FromBase64String(Encoding.ASCII.GetString(fp.Payload));
                            tf.FS.Write(fp.Payload, 0, fp.PayloadLength);
                            tf.CurrLength += fp.PayloadLength;

                            Console.WriteLine(tf.Length.ToString() + ":" + tf.CurrLength.ToString());

                            if (tf.CurrLength >= tf.Length)//文件传输完成
                            {
                                tf.FS.Flush();
                                CloseFileStream(tf.FS);//关闭文件流
                                string fullFileName = OfflineFolder + @"\" + tf.MD5 + tf.Extension;//标记文件路径
                                string CacheFullFileName = CacheFolder + @"\" + tf.MD5 + tf.Extension;//标记文件路径

                                if (File.Exists(fullFileName))
                                {
                                    File.Delete(fullFileName);
                                    System.Threading.Thread.Sleep(500);
                                }

                                if (File.Exists(CacheFullFileName))
                                    File.Move(CacheFullFileName, fullFileName);//拷贝文件

                                fp.CommandType = (byte)FileServerCommandType.Over;//标识文件已经传输完成
                                Sessions.SendTo(fp.Header, session.ID);//通知客户端停止上传
                                Sessions.CloseSession(session.ID);
                            }
                            else//通知用户上传文件
                            {
                                ///////////////////////
                                //通知用户上传文件
                                fp.CommandType = (byte)FileServerCommandType.Set;
                                Sessions.SendTo(fp.Header, session.ID);//通知客户端停止上传
                                ///////////////////////
                            }
                        }
                    }
                    break;
                case (byte)FileServerCommandType.Over://下载文件请求
                    {

                    }
                    break;
                case (byte)FileServerCommandType.Get://下载文件请求
                    {
                        if (session.Tag == null)
                        {
                            if (fp.PayloadLength < BinaryFileMsg.HeaderLength)
                            {
                                Sessions.CloseSession(session.ID);
                                return;
                            }
                            BinaryFileMsg FileMsg = new BinaryFileMsg(fp.Payload);
                            string fullFileName = OfflineFolder + @"\" + FileMsg.MD5 + FileMsg.Extension;//标记文件路径
                            if (!File.Exists(fullFileName))
                            {
                                Sessions.CloseSession(session.ID);
                                return;
                            }

                            #region 创建文件只读流
                            OfflineFileInfo tfi = new OfflineFileInfo();
                            tfi.FS = new FileStream(fullFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                            session.Tag = tfi;
                            #endregion 
                        }

                        var tf = session.Tag as OfflineFileInfo;
                        if (tf == null)
                        {
                            Sessions.CloseSession(session.ID);
                            return;
                        }

                        if (tf.buffer.Length > tf.FS.Length - tf.CurrLength)
                            tf.buffer = new byte[tf.FS.Length - tf.CurrLength];

                        ///读文件到缓冲区
                        tf.FS.Read(tf.buffer, 0, tf.buffer.Length);
                        fp.Payload = tf.buffer;// Encoding.ASCII.GetBytes(Convert.ToBase64String(tf.buffer));
                        tf.CurrLength += tf.buffer.Length;
                        Sessions.SendTo(fp.ToArray(), session.ID);

                        if (tf.CurrLength >= tf.FS.Length)
                        {
                            fp.CommandType = (byte)FileServerCommandType.Over;//标识文件已经传输完成
                            Sessions.SendTo(fp.Header, session.ID);//通知客户端停止上传
                            CloseFileStream(tf.FS);//关闭文件流
                            Sessions.CloseSession(session.ID);
                        }

                    }
                    break;
            } 
        }

        #endregion

        #region 关闭文件流
        /// <summary>
        /// 关闭文件流
        /// </summary>
        private void CloseFileStream(FileStream FS)
        {
            if (FS != null)
            { FS.Close(); FS.Dispose(); FS = null; }
        }
        #endregion

        #region 释放资源
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (WSServer  != null) 
                WSServer.Stop();  

            if (TimerFileServer != null)
            {
                TimerFileServer.Enabled = false;//停止文件服务检测
                TimerFileServer.Dispose();
                TimerFileServer = null;
            } 
        }
        #endregion

        #region 停止服务
        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            if (WSServer != null)
                WSServer.Stop();
            if (TimerFileServer != null)
            {
                TimerFileServer.Elapsed -= new System.Timers.ElapsedEventHandler(TimerFileServer_Elapsed);
                TimerFileServer.Enabled = false; TimerFileServer.Close(); TimerFileServer.Dispose(); TimerFileServer = null; }//停止文件服务检测
        }
        #endregion

        #endregion
 
        #region 缓冲服务周期
        private void TimerFileServer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (IsCheck)
                return;
            IsCheck = true;


            IsCheck = false ;
        }
        #endregion
         
    }
}
